#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<unistd.h>
#include<sys/select.h>

#define TTY_PATH  "/dev/tty"          
#define STTY_US  "stty raw -echo -F "   
#define STTY_DEF  "stty -raw echo -F "  

#define WIDE 18
#define HEIGHT 20
#define INFO_AREA 6
#define SPEED 8
#define Y_NEXT 4
#define X_NEXT WIDE - INFO_AREA / 2 - 1

int score = 0;                     
int map[HEIGHT][WIDE] = { 0 };  

int x, y;                          
int moveDown_count = 0;
int shapeIndex, shapeIndex_next;  

struct Point {
    int shape_x;
    int shape_y;
};

// 定义图形的形状偏移
struct Point shapes[19][4] = {
    {{0, 0},{-1, 0},{1, 0},{2, 0}},      // 横条
    {{0, 0},{0, -1},{0, 1},{0, 2}},      // 竖条
    {{0,0},{-1,-1},{-1,0},{0,-1}},     // 方块
    {{0, 0},{0, -1},{0, -2},{1, 0}},    // 正Ｌ1
    {{0,0},{0,1},{1,0},{2,0}},           // 正Ｌ2
    {{0,0},{-1,0},{0,1},{0,2}},         // 正Ｌ3
    {{0,0},{0,-1},{-1,0},{-2,0}},      // 正Ｌ4
    {{0,0},{-1,0},{0,-1},{0,-2}},      // 反Ｌ1
    {{0,0},{0,-1},{1,0},{2,0}},         // 反Ｌ2
    {{0,0},{1,0},{0,1},{0,2}},           // 反Ｌ3
    {{0,0},{-1,0},{-2,0},{0,1}},        // 反Ｌ4
    {{0,0},{-1,0},{1,0},{0,-1}},        // Ｔ1
    {{0,0},{0,1},{0,-1},{1,0}},         // Ｔ2
    {{0,0},{-1,0},{1,0},{0,1}},         // Ｔ3
    {{0,0},{-1,0},{0,-1},{0,1}},        // Ｔ4
    {{0,0},{1,0},{0,-1},{-1,-1}},      // 正 Z1
    {{0,0},{1,-1},{0,1},{1,0}},         // 正Ｚ2
    {{0,0},{1,-1},{-1,0},{0,-1}},      // 反Ｚ1
    {{0,0},{-1,-1},{-1,0},{0,1}}       // 反Ｚ2
};

// 函数声明
void setFrame();
void showMap();
void showPoint(int x, int y, int bright, int color);
void showShape();
void createNewShape();
int moveDown();
void addToMap();
int overCheck();
int getChar();
void control(char str);
void changeShape();
void moveLeft();
void moveRight();
void clearLines();

int main() {
    system(STTY_US TTY_PATH);  // 输入阻塞
    setFrame();                                      // 初始化map
    showMap();                                       // 展示界面
    srand(time(0));
    shapeIndex = rand() % 19;
    createNewShape();                        // 创建下一个图形

    while(1) {
        system("clear");                        // 清除界面显示内容
        showShape();                             // 展示方块
        showMap();                               // 展示界面

        if (moveDown()) {                      // 判断是否可以下移
            addToMap();                         // 如果不能下移，更新map
            if (overCheck()) {                  // 检查是否游戏结束
                break;
            }
            clearLines();                      // 清理已满行
            shapeIndex = shapeIndex_next;
            createNewShape();                   // 创建下一个图形
        }

        char str = getChar();                  // 获取输入指令
        if (str == 3) {                        // 检测Ctrl+C
            break;
        }
        control(str);                          // 控制图形的移动
        usleep(1000 * 50);                     // 限制游戏速度
    }

    system(STTY_DEF TTY_PATH);  // 结束输入阻塞
    printf("\033[21;0H");
}

// 设置边框
void setFrame() {
    for (int i = 0; i < WIDE; i++) {
        map[0][i] = 1;                   // 上边界
        map[HEIGHT - 1][i] = 1;          // 下边界
    }
    for (int i = 0; i < HEIGHT; i++) {
        map[i][0] = 1;                   // 左边界
        map[i][WIDE - INFO_AREA - 1] = 1; // 右边界
        map[i][WIDE - 1] = 1;            // 右边界
    }
}

// 展示游戏界面
void showMap() {
    for (int i = 0; i < HEIGHT; i++) {
        for (int j = 0; j < WIDE; j++) {
            if (map[i][j] == 1 || map[i][j] == 2) {
                showPoint(j, i, 1, 32 + map[i][j]);  // 输出图形
            }
        }
    }

    printf("\033[2;27H");           // 显示Score与Next的区域
    printf("\033[31mNext:"); 
    printf("\033[11;27H");
    printf("\033[32mScore:");   
    printf("\033[12;26H"); 
    printf(" \033[32m%3d", score);   
    fflush(stdout);  // 清理缓存
}

// 输出一个方块
void showPoint(int x1, int y1, int bright, int color) {
    printf("\033[%d;%dH", y1 + 1, x1 * 2 + 1);  // 设置光标位置
    printf("\033[%d;%dm■ \033[0m", bright, color);  // 输出图形，带颜色
}

// 创建新图形
void createNewShape() {
    y = 2;
    x = (WIDE - INFO_AREA) / 2;
    shapeIndex_next = rand() % 19;
}

// 展示正在下降的图形与下一个图形
void showShape() {
    for (int i = 0; i < 4; i++) {
        showPoint(x + shapes[shapeIndex][i].shape_x, y + shapes[shapeIndex][i].shape_y, 1, 31);  // 正在下降的图形
    }
    for (int i = 0; i < 4; i++) {
        showPoint(X_NEXT + shapes[shapeIndex_next][i].shape_x, Y_NEXT + shapes[shapeIndex_next][i].shape_y, 1, 31);  // 下一个图形
    }
}

// 判断是否可以继续下移
int moveDown() {
    if (moveDown_count < SPEED) {
        moveDown_count++;
        return 0;
    }

    moveDown_count = 0;
    for (int i = 0; i < 4; i++) {
        int dx = x + shapes[shapeIndex][i].shape_x;
        int dy = y + shapes[shapeIndex][i].shape_y + 1;
        if (map[dy][dx] == 1 || map[dy][dx] == 2) {
            return 1;  // 不可移动
        }
    }
    y++;
    return 0;
}

// 将当前图形加入到map中
void addToMap() {
    for (int i = 0; i < 4; i++) {
        map[y + shapes[shapeIndex][i].shape_y][x + shapes[shapeIndex][i].shape_x] = 2;
    }
}

// 检查是否游戏结束
int overCheck() {
    for (int i = 0; i < WIDE; i++) {
        if (map[1][i] == 2) {
            return 1;  // 游戏结束
        }
    }
    return 0;
}

// 获取用户输入
int getChar() {
    fd_set rfds;
    struct timeval tv;
    int ch = 0;
    FD_ZERO(&rfds);
    FD_SET(0, &rfds);
    tv.tv_sec = 0;
    tv.tv_usec = 10;  // 设置超时时间为10微秒
    if (select(1, &rfds, NULL, NULL, &tv) > 0) {
        ch = getchar();
    }
    return ch;
}

// 根据输入控制图形移动
void control(char str) {
    switch (str) {
        case 'w':  // 变形
            changeShape();
            break;
        case 'a':  // 左移
            moveLeft();
            break;
        case 's':  // 下移
            moveDown_count += SPEED;
            break;
        case 'd':  // 右移
            moveRight();
            break;
    }
}

// 变形操作
void changeShape() {
    int ts = shapeIndex;
    switch(ts) {
        case 0: ts++; break;
        case 1: ts = 0; break;
        case 2: break;
        case 3: case 4: case 5: ts++; break;
        case 6: ts = 3; break;
        case 7: case 8: case 9: ts++; break;
        case 10: ts = 7; break;
        case 11: case 12: case 13: ts++; break;
        case 14: ts = 11; break;
        case 15: ts++; break;
        case 16: ts = 15; break;
        case 17: ts++; break;
        case 18: ts = 17; break;
    }

    // 判断变形后是否会发生碰撞
    for (int i = 0; i < 4; i++) {
        int cx = x + shapes[ts][i].shape_x;
        int cy = y + shapes[ts][i].shape_y;
        if (map[cy][cx] == 1 || map[cy][cx] == 2) {
            return;  // 碰撞，不能变形
        }
    }
    shapeIndex = ts;  // 完成变形
}

// 左移操作
void moveLeft() {
    for (int i = 0; i < 4; i++) {
        if (map[y + shapes[shapeIndex][i].shape_y][x + shapes[shapeIndex][i].shape_x - 1] == 1 ||
            map[y + shapes[shapeIndex][i].shape_y][x + shapes[shapeIndex][i].shape_x - 1] == 2) {
            return;
        }
    }
    x--;  // 执行左移
}

// 右移操作
void moveRight() {
    for (int i = 0; i < 4; i++) {
        if (map[y + shapes[shapeIndex][i].shape_y][x + shapes[shapeIndex][i].shape_x + 1] == 1 ||
            map[y + shapes[shapeIndex][i].shape_y][x + shapes[shapeIndex][i].shape_x + 1] == 2) {
            return;
        }
    }
    x++;  // 执行右移
}

// 清理已满行
void clearLines() {
    for (int i = HEIGHT - 2; i > 0; i--) {
        int full = 1;
        for (int j = 1; j < WIDE - INFO_AREA - 1; j++) {
            if (map[i][j] != 2) {
                full = 0;
                break;
            }
        }
        
        if (full) {
            score++;  // 得分
            for (int n = i; n > 1; n--) {
                for (int j = 0; j < WIDE - INFO_AREA - 1; j++) {
                    map[n][j] = map[n - 1][j];  // 上面的行下移
                }
            }
            i++;  // 再检查一次当前行
        }
    }
    fflush(stdout);  // 清理缓存
}

